/**
 * File Comment: major implementation is done
 */
package edu.gatech.oad.project.operations.users;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import edu.gatech.oad.project.coordinator.Office;

/**
 * This class is a cache for all the recent users
 * created, added, or updated by the system. Periodically,
 *  the cache will sync itself with the outlying database
 *  (when we actually have one). Holds four separate caches:
 *  one for a patient, one for a nurse, and one for system
 *  admins
 * 
 * @author Group 17
 * @version 1.0
 */
public class UserCache {

    /**
     * Field patients: cache of patients, stored by name
     */
    private final Map<String, List<Patient>> patients;
    
    /**
     * Field nurses: cache of nurses, stored by name
     */
    private final Map<String, List<Nurse>> nurses;
    
    /**
     * Field doctors: cache of doctors, stored by name
     */
    private final Map<String, List<Doctor>> doctors;
    
    /**
     * Field administrators: cache of admins, stored by name
     */
    private final Map<String, List<SystemAdmin>> administrators;
    
    /**
     * Field patientIDs: maps an id to a username
     */
    private final Map<Integer, String> patientIDs;
    
    /**
     * Field nurseIDs: maps an id to a username
     */
    private final Map<Integer, String> nurseIDs;
    
    /**
     * Field doctorIDs: maps an id to a username
     */
    private final Map<Integer, String> doctorIDs;
    
    /**
     * Field adminIDs: maps an id to a username
     */
    private final Map<Integer, String> adminIDs;
    
    /**
     * Field myLogger: holds the logger for the user cache file
     */
    private static final Logger MYLOGGER = Logger.getLogger("edu.gatech." +
            "oad.project.operations.users");
    
	/**
	 * Default constructor that initializes the
	 * four caches. These are implemented as Hash Tables
	 */
	public UserCache() {
		this.patients = new HashMap<String, List<Patient>>();
		this.nurses = new HashMap<String, List<Nurse>>();
		this.doctors = new HashMap<String, List<Doctor>>();
		this.administrators = new HashMap<String, List<SystemAdmin>>();
		
		this.patientIDs = new HashMap<Integer, String>();
		this.nurseIDs = new HashMap<Integer, String>();
		this.doctorIDs = new HashMap<Integer, String>();
		this.adminIDs = new HashMap<Integer, String>();
		
        MYLOGGER.addHandler(Office.FILEHANDLER);
        MYLOGGER.setLevel(Level.ALL);
        MYLOGGER.finest("Cache succesfully created");
	}
	
	/**
	 * Adds a patient into the cache. Only
	 * adds the patient if it doesn't already
	 * exist (which is determined based on the
	 * patients id).
	 * 
	 * @param p the patient
	 */
	public void addPatient(Patient p) {
	    final String name = p.getName();
	    final int id = p.getID();
	    
	    if(!this.patientIDs.containsKey(id)){
	        this.patientIDs.put(id, name);
	        if (this.patients.containsKey(name)) {
	            this.patients.get(name).add(p);
	        }
	        else {
	            final List<Patient> newList = new ArrayList<Patient>();
	            newList.add(p);
	            this.patients.put(name, newList);
	        }
	        MYLOGGER.info("Successfully added a new patient");
	    }
	    else{
	        MYLOGGER.info("The patient already existed in the cache");
	    }
	    
	}
	
	/**
	 * Removes a patient based on the id of the patient
	 * 
	 * @param p the patient
	 */
	public void removePatient(Patient p){
	    final String name = p.getName();
	    final int id = p.getID();
	    
	    if(this.patientIDs.containsKey(id)){
	        this.patientIDs.remove(id);
	        final List<Patient> pats = this.patients.get(name);
	        for(int i = 0; i < pats.size(); i++) {
	            Patient temp = pats.get(i);
	            if (temp.getID() == id) {
	                pats.remove(temp);
	                MYLOGGER.info("Successfully removed a patient");
	                break;
	            }
	        }
            
        }
        else{
            MYLOGGER.info("The patient did not exist in the cache");
        }
	}
	
	/**
	 * Finds a list of patients in the cache based on
	 * their name.
	 * 
	 * @param name the name of the patients
	 * @return the user list of patients
	 */
	public List<Patient> getPatientsByName(String name) {
	    final List<Patient> temp = this.patients.get(name);
	    
	    if (temp != null){
	        MYLOGGER.info("Found patients");
	    }
	    else{
	        MYLOGGER.info("Patients did not exist");
	    }
	    
	    return temp;
	}
	
	/**
	 * Finds and returns a patient based on the
	 * patients id
	 * 
	 * @param id the id of the patient
	 * @return the patient object
	 */
	public Patient getPatient(int id) {
	    if (this.patientIDs.containsKey(id)) {
	        final String name = this.patientIDs.get(id);
	        final List<Patient> pats = this.patients.get(name);
	        for (int i = 0; i < pats.size(); i++) {
	            Patient temp = pats.get(i);
	            if (temp.getID() == id) {
	                MYLOGGER.info("Found patient");
	                return temp;
	            }
	        }
	    }
	    
	    MYLOGGER.info("Patient did not exist");
	    return null;
	}
	
	   /**
     * Adds a nurse into the cache. Only
     * adds the nurse if it doesn't already
     * exist
     * 
     * @param n the nurse
     */
    public void addNurse(Nurse n) {
        final String name = n.getName();
        final int id = n.getID();
        
        if(!this.nurseIDs.containsKey(id)){
            this.nurseIDs.put(id, name);
            if (this.nurses.containsKey(name)) {
                this.nurses.get(name).add(n);
            }
            else {
                final List<Nurse> newList = new ArrayList<Nurse>();
                newList.add(n);
                this.nurses.put(name, newList);
            }
            MYLOGGER.info("Successfully added a new nurse");
        }
        else{
            MYLOGGER.info("The nurse already existed in the cache");
        }
        
    }
    
    /**
     * Removes a nurse based on the if of the nurse
     * 
     * @param n the nurse
     */
    public void removeNurse(Nurse n){
        final String name = n.getName();
        final int id = n.getID();
        
        if(this.nurseIDs.containsKey(id)){
            this.nurseIDs.remove(id);
            final List<Nurse> nurs = this.nurses.get(name);
            for(int i = 0; i < nurs.size(); i++) {
                Nurse temp = nurs.get(i);
                if (temp.getID() == id) {
                    nurs.remove(temp);
                    MYLOGGER.info("Successfully removed a nurse");
                    break;
                }
            }
            
        }
        else{
            MYLOGGER.info("The nurse did not exist in the cache");
        }
    }
    
    /**
     * Finds a list of nurses in the cache based on
     * their name.
     * 
     * @param name the name of the nurses
     * @return the user list of nurses
     */
    public List<Nurse> getNursesByName(String name) {
        final List<Nurse> temp = this.nurses.get(name);
        
        if (temp != null){
            MYLOGGER.info("Found nurses");
        }
        else{
            MYLOGGER.info("Nursea did not exist");
        }
        
        return temp;
    }
    
    /**
     * Finds and returns a nurse based on the
     * nurses id
     * 
     * @param id the id of the nurse
     * @return the nurse object
     */
    public Nurse getNurse(int id) {
        if (this.nurseIDs.containsKey(id)) {
            final String name = this.nurseIDs.get(id);
            final List<Nurse> nurs = this.nurses.get(name);
            for (int i = 0; i < nurs.size(); i++) {
                Nurse temp = nurs.get(i);
                if (temp.getID() == id) {
                    MYLOGGER.info("Nurse Found");
                    return temp;
                }
            }
        }
        
        MYLOGGER.info("Nurse did not exist");
        return null;
    }
	
    /**
     * Adds a doctor into the cache. Only
     * adds the doctor if it doesn't already
     * exist
     * 
     * @param d the doctor
     */
    public void addDoctor(Doctor d) {
        final String name = d.getName();
        final int id = d.getID();
        
        if(!this.doctorIDs.containsKey(id)){
            this.doctorIDs.put(id, name);
            if (this.doctors.containsKey(name)) {
                this.doctors.get(name).add(d);
            }
            else {
                final List<Doctor> newList = new ArrayList<Doctor>();
                newList.add(d);
                this.doctors.put(name, newList);
            }
            MYLOGGER.info("Successfully added a new doctor");
        }
        else{
            MYLOGGER.info("The doctor already existed in the cache");
        }
        
    }
    
    /**
     * Removes a doctor based on the id of the doctor
     * 
     * @param d the doctor
     */
    public void removeDoctor(Doctor d){
        final String name = d.getName();
        final int id = d.getID();
        
        if(this.doctorIDs.containsKey(id)){
            this.doctorIDs.remove(id);
            final List<Doctor> doc = this.doctors.get(name);
            for(int i = 0; i < doc.size(); i++) {
                Doctor temp = doc.get(i);
                if (temp.getID() == id) {
                    doc.remove(temp);
                    MYLOGGER.info("Successfully removed a doctor");
                    break;
                }
            }
            
        }
        else{
            MYLOGGER.info("The doctor did not exist in the cache");
        }
    }
    
    /**
     * Finds a list of doctors in the cache based on
     * their name.
     * 
     * @param name the name of the doctors
     * @return the user list of doctors
     */
    public List<Doctor> getDoctorsByName(String name) {
        final List<Doctor> temp = this.doctors.get(name);
        
        if (temp != null){
            MYLOGGER.info("Found doctors");
        }
        else{ 
            MYLOGGER.info("doctors did not exist");
        }
        
        return temp;
    }
    
    /**
     * Finds and returns a doctor based on the
     * doctors id
     * 
     * @param id the id of the doctor
     * @return the doctor object
     */
    public Doctor getDoctor(int id) {
        if (this.doctorIDs.containsKey(id)) {
            final String name = this.doctorIDs.get(id);
            final List<Doctor> doc = this.doctors.get(name);
            for (int i = 0; i < doc.size(); i++) {
                Doctor temp = doc.get(i);
                if (temp.getID() == id) {
                    MYLOGGER.info("Doctor Found");
                    return temp;
                }
            }
        }
        
        MYLOGGER.info("Doctor did not exist");
        return null;
    }
    
    /**
     * Adds a SysAdmin into the cache. Only
     * adds the SysAdmin if it doesn't already
     * exist
     * 
     * @param sa the SysAdmin
     */
    public void addSystemAdmin(SystemAdmin sa) {
        final String name = sa.getName();
        final int id = sa.getID();
        
        if(!this.adminIDs.containsKey(id)){
            this.adminIDs.put(id, name);
            if (this.administrators.containsKey(name)) {
                this.administrators.get(name).add(sa);
            }
            else {
                final List<SystemAdmin> newList = new ArrayList<SystemAdmin>();
                newList.add(sa);
                this.administrators.put(name, newList);
            }
            MYLOGGER.info("Successfully added a new admin");
        }
        else{
            MYLOGGER.info("The admin already existed in the cache");
        }
        
    }
    
    /**
     * Removes a SysAdmin based id of the admin
     * 
     * @param sa the system admin
     */
    public void removeSystemAdmin(SystemAdmin sa){
        final String name = sa.getName();
        final int id = sa.getID();
        
        if(this.adminIDs.containsKey(id)){
            this.adminIDs.remove(id);
            final List<SystemAdmin> adm = this.administrators.get(name);
            for(int i = 0; i < adm.size(); i++) {
                SystemAdmin temp = adm.get(i);
                if (temp.getID() == id) {
                    adm.remove(temp);
                    MYLOGGER.info("Successfully removed a admin");
                    break;
                }
            }
            
        }
        else{
            MYLOGGER.info("The admin did not exist in the cache");
        }
    }
    
    /**
     * Finds a list of admins in the cache based on
     * their name.
     * 
     * @param name the name of the admins
     * @return the user list of admins
     */
    public List<SystemAdmin> getSystemAdminsByName(String name) {
        final List<SystemAdmin> temp = this.administrators.get(name);
        
        if (temp != null){
            MYLOGGER.info("Found admins");
        }
        else{
            MYLOGGER.info("admins did not exist");
        }
        
        return temp;
    }
    
    /**
     * Finds and returns a admin based on the
     * admins id
     * 
     * @param id the id of the admin
     * @return the admin object
     */
    public SystemAdmin getSystemAdmin(int id) {
        if (this.adminIDs.containsKey(id)) {
            final String name = this.adminIDs.get(id);
            final List<SystemAdmin> adm = this.administrators.get(name);
            for (int i = 0; i < adm.size(); i++) {
                SystemAdmin temp = adm.get(i);
                if (temp.getID() == id) {
                    MYLOGGER.info("admin Found");
                    return temp;
                }
            }
        }
        
        MYLOGGER.info("admin did not exist");
        return null;
    }
    
	/**
	 * String representation of a user cache
	 * 
	 * @return the string representation
	 */
	@Override
	public String toString() {
	    return "Cache of patients, nurses, doctors, and system admins";
	}

	/**
	 * for testing purposes only
	 * 
	 * @return the patients
	 */
	public Map<String, List<Patient>> getPatientsInCache(){
	    return this.patients;
	}

	/**
	 * For testing purposes
	 * 
	 * @return the current user
	 */
	// TEMP, WILL DELETE
    public User getCurUser() {
        // TODO Auto-generated method stub
        return null;
    }
}
